#include "signaturelabel.h"
#include <QPainter>
#include <QDebug>
#include <QResizeEvent>
#include <QGuiApplication>
#include <QScreen>
#include <QBuffer>

/**
 * @brief 高质量路径平滑算法
 * @param path 待平滑的路径
 * @param level 平滑级别（1-5）
 *
 * 使用Catmull-Rom样条曲线算法进行路径平滑：
 * 1. 算法基于前后四个点计算曲线控制点
 * 2. 平滑级别越高，曲线越光滑但计算量越大
 * 3. 级别参数影响控制点的距离系数
 *
 * 公式：
 *   c1 = p1 + t * (p2 - p0)
 *   c2 = p2 - t * (p3 - p1)
 *   其中 t = 0.5 * (1.0 / level)
 *
 * 然后使用三次贝塞尔曲线连接点
 */
static void smoothPath(QPainterPath &path, int level) {
    // 检查是否需要平滑：级别为0或点数不足3个时不处理
    if (level <= 0 || path.elementCount() < 3) return;

    QPainterPath smoothed;
    smoothed.moveTo(path.elementAt(0)); // 起点不变

    // 遍历路径中的每个点（跳过首尾点）
    for (int i = 1; i < path.elementCount() - 1; ++i) {
        // 获取当前点周围的四个点：
        const QPointF p0 = (i > 1) ? path.elementAt(i-2) : path.elementAt(i-1);
        const QPointF p1 = path.elementAt(i-1);
        const QPointF p2 = path.elementAt(i);
        const QPointF p3 = (i < path.elementCount()-2) ? path.elementAt(i+1) : p2;

        // 计算控制点（基于Catmull-Rom样条算法）
        const double t = 0.5 * (1.0 / level); // 平滑系数，级别越高t越小
        QPointF c1 = p1 + t * (p2 - p0); // 前向控制点
        QPointF c2 = p2 - t * (p3 - p1); // 后向控制点

        // 使用三次贝塞尔曲线连接点
        smoothed.cubicTo(c1, c2, p2);
    }

    // 用平滑后的路径替换原始路径
    path = smoothed;
}

/**
 * @brief 构造函数
 * @param parent 父部件指针
 *
 * 初始化签名区域：
 * 1. 设置默认画笔（黑色，3像素宽，圆角）
 * 2. 设置最小尺寸
 * 3. 初始化画布
 */
SignatureLabel::SignatureLabel(QWidget *parent)
    : QLabel(parent)
{
    // 初始化画笔：黑色、3像素宽、实线、圆角端点、圆角连接
    m_currentPen = QPen(Qt::black, 3.0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);

    // 设置最小尺寸（确保有足够的绘制空间）
    setMinimumSize(150, 100);

    // 初始化画布（双缓冲技术）
    initCanvas();
}

/// @brief 析构函数（目前无需特殊清理）
SignatureLabel::~SignatureLabel()
{
}

/**
 * @brief 初始化画布
 *
 * 1. 根据设备像素比创建高分辨率画布
 * 2. 设置画布的设备像素比（处理高DPI屏幕）
 * 3. 填充透明或背景色
 */
void SignatureLabel::initCanvas()
{
    // 获取设备像素比（高DPI支持）
    qreal dpr = devicePixelRatioF();

    // 创建高分辨率画布（尺寸为控件尺寸 * 设备像素比）
    m_canvas = QPixmap(size() * dpr);

    // 设置画布的设备像素比（确保在不同DPI屏幕正确显示）
    m_canvas.setDevicePixelRatio(dpr);

    // 填充背景（透明或背景色）
    clearSignature();
}

/**
 * @brief 设置画笔颜色
 * @param color 新的画笔颜色
 *
 * 如果颜色改变，更新界面
 */
void SignatureLabel::setPenColor(const QColor &color)
{
    if (m_currentPen.color() != color) {
        m_currentPen.setColor(color);
        update(); // 触发重绘
    }
}

/**
 * @brief 设置画笔宽度
 * @param width 新的画笔宽度（1-20像素）
 *
 * 宽度必须在有效范围内，如果改变则更新界面
 */
void SignatureLabel::setPenWidth(int width)
{
    // 检查宽度是否有效且在范围内
    if (width > 0 && width <= 20 && m_currentPen.width() != width) {
        m_currentPen.setWidthF(width); // 设置浮点宽度（更精确）
        update(); // 触发重绘
    }
}

/**
 * @brief 设置画笔样式
 * @param style 画笔样式枚举值
 *
 * 如果样式改变，更新界面
 */
void SignatureLabel::setPenStyle(PenStyle style)
{
    // 转换为Qt的画笔样式并检查是否改变
    if (static_cast<Qt::PenStyle>(style) != m_currentPen.style()) {
        m_currentPen.setStyle(static_cast<Qt::PenStyle>(style));
        update(); // 触发重绘
    }
}

/**
 * @brief 设置平滑级别
 * @param level 新的平滑级别（0-5）
 *
 * 0 = 无平滑
 * 5 = 最大平滑
 * 实际平滑在鼠标释放时应用
 */
void SignatureLabel::setSmoothingLevel(int level)
{
    m_smoothingLevel = level;
}

/**
 * @brief 设置背景颜色
 * @param color 新的背景颜色
 *
 * 如果背景不透明，颜色改变时重新渲染画布并更新界面
 */
void SignatureLabel::setBackgroundColor(const QColor &color)
{
    if (m_bgColor != color) {
        m_bgColor = color;
        renderToCanvas(); // 重新渲染画布
        update();         // 触发重绘
    }
}

/**
 * @brief 设置背景透明
 * @param transparent 是否透明
 *
 * 改变背景透明状态时重新渲染画布并更新界面
 */
void SignatureLabel::setBackgroundTransparent(bool transparent)
{
    if (m_transparentBg != transparent) {
        m_transparentBg = transparent;
        renderToCanvas(); // 重新渲染画布
        update();         // 触发重绘
    }
}

/// @brief 获取当前画笔颜色
QColor SignatureLabel::penColor() const
{
    return m_currentPen.color();
}

/// @brief 获取当前画笔宽度（整数）
int SignatureLabel::penWidth() const
{
    return static_cast<int>(m_currentPen.widthF());
}

/// @brief 获取当前画笔样式
SignatureLabel::PenStyle SignatureLabel::penStyle() const
{
    return static_cast<PenStyle>(m_currentPen.style());
}

/// @brief 获取背景颜色
QColor SignatureLabel::backgroundColor() const
{
    return m_bgColor;
}

/// @brief 检查背景是否透明
bool SignatureLabel::isBackgroundTransparent() const
{
    return m_transparentBg;
}

/**
 * @brief 获取签名图像
 * @param cropped 是否裁剪到签名区域
 * @return 签名图像
 *
 * 1. 如果没有签名，返回空图像
 * 2. 如果不需要裁剪，返回完整画布
 * 3. 裁剪时：计算签名边界，添加15%边距，创建裁剪图像
 */
QPixmap SignatureLabel::signatureImage(bool cropped) const
{
    // 检查是否有签名内容
    if (m_undoStack.isEmpty()) {
        return QPixmap(); // 返回空图像
    }

    // 如果不需要裁剪，直接返回整个画布
    if (!cropped) {
        return m_canvas;
    }

    // 计算签名内容的边界矩形（使用浮点数计算）
    QRect bounds = calculateSignatureBounds();
    if (bounds.isEmpty()) {
        return QPixmap(); // 空边界返回空图像
    }

    // 关键修改：扩大边界矩形，包含画笔宽度
    int penMargin = qMax(1, penWidth() / 2); // 至少1像素边距
    bounds.adjust(-penMargin, -penMargin, penMargin, penMargin);

    // 增加动态边距计算
    double marginRatio = 0.15; // 15%边距
    int minMargin = 5;        // 最小5像素边距

    // 计算动态边距（取最大值保证足够空间）
    int margin = qMax(
        static_cast<int>(qMax(bounds.width(), bounds.height()) * marginRatio),
        minMargin
    );

    // 创建包含边距的裁剪尺寸
    QSize cropSize = bounds.size() + QSize(2 * margin, 2 * margin);

    // 计算签名在裁剪区域中的居中位置
    QPoint signaturePos(
        (cropSize.width() - bounds.width()) / 2,
        (cropSize.height() - bounds.height()) / 2
    );

    // 获取设备像素比（高DPI支持）
    qreal dpr = devicePixelRatioF();

    // 创建裁剪后的图像
    QPixmap croppedImg(cropSize * dpr);
    croppedImg.setDevicePixelRatio(dpr);
    croppedImg.fill(Qt::transparent);

    // 将签名区域绘制到裁剪图像
    QPainter painter(&croppedImg);
    painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);

    // 计算源区域（设备像素坐标）
    QRect sourceRect(
        bounds.left() * dpr,
        bounds.top() * dpr,
        bounds.width() * dpr,
        bounds.height() * dpr
    );

    // 计算目标区域（设备像素坐标）
    QRect targetRect(
        signaturePos.x() * dpr,
        signaturePos.y() * dpr,
        bounds.width() * dpr,
        bounds.height() * dpr
    );

    // 绘制签名到目标位置
    painter.drawPixmap(targetRect, m_canvas, sourceRect);

    return croppedImg;
}

/**
 * @brief 获取超高质量签名图像
 * @return 4倍分辨率的签名图像
 *
 * 1. 计算签名边界并添加20%边距
 * 2. 创建4倍分辨率的图像
 * 3. 使用抗锯齿渲染所有笔画
 */
QImage SignatureLabel::signatureImageHQ() const
{
    // 计算签名边界
    QRect bounds = calculateSignatureBounds();
    if (bounds.isEmpty()) {
        return QImage();
    }

    // 添加20%边距
    int margin = qMax(bounds.width(), bounds.height()) * 0.20;
    bounds.adjust(-margin, -margin, margin, margin);

    // 获取设备像素比
    qreal dpr = devicePixelRatioF();

    // 计算签名中心点（设备像素坐标）
    QPointF signatureCenter = QRectF(bounds).center() * dpr;

    // 创建4倍分辨率的图像
    const int scaleFactor = 4;
    QSize imageSize = bounds.size() * scaleFactor;
    QImage image(imageSize, QImage::Format_ARGB32_Premultiplied);
    image.fill(Qt::transparent);

    QPainter painter(&image);
    painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing |
                          QPainter::SmoothPixmapTransform);

    // 设置视图矩阵：先缩放后平移
    QTransform transform;

    // 1. 移动到图像中心
    transform.translate(imageSize.width() / 2.0, imageSize.height() / 2.0);

    // 2. 应用缩放
    transform.scale(scaleFactor, scaleFactor);

    // 3. 移动到签名中心负位置（使签名居中）
    transform.translate(-signatureCenter.x(), -signatureCenter.y());

    painter.setTransform(transform);

    // 绘制所有笔画
    for (const SignatureStroke &stroke : m_undoStack) {
        painter.setPen(stroke.pen);
        painter.drawPath(stroke.path);
    }

    return image;
}

/**
 * @brief 获取矢量路径
 * @return 包含所有笔画的矢量路径
 *
 * 将撤销栈中的所有笔画路径合并为一个完整路径
 */
QPainterPath SignatureLabel::signaturePath() const
{
    QPainterPath combined;
    for (const SignatureStroke &stroke : m_undoStack) {
        combined.addPath(stroke.path);
    }
    return combined;
}

/**
 * @brief 清除签名内容
 *
 * 1. 清空撤销/重做栈
 * 2. 重置当前路径
 * 3. 清空画布并填充背景
 * 4. 发送签名变化信号
 */
void SignatureLabel::clearSignature()
{
    // 清空历史记录
    m_undoStack.clear();
    m_redoStack.clear();
    m_currentPath = QPainterPath();

    // 用背景色或透明填充画布
    m_canvas.fill(m_transparentBg ? Qt::transparent : m_bgColor);

    // 更新界面
    update();

    // 发送变化信号
    emit signatureChanged();
    emit undoStateChanged();
}

/**
 * @brief 撤销操作
 *
 * 1. 从撤销栈弹出最后一个笔画到重做栈
 * 2. 重新渲染画布
 * 3. 更新界面并发送信号
 */
void SignatureLabel::undo()
{
    if (m_undoStack.isEmpty()) return;

    // 从撤销栈移动到重做栈
    m_redoStack.push(m_undoStack.pop());

    // 重新渲染画布（不包含撤销的笔画）
    renderToCanvas();

    // 更新界面
    update();

    // 发送变化信号
    emit signatureChanged();
    emit undoStateChanged();
}

/**
 * @brief 重做操作
 *
 * 1. 从重做栈弹出最后一个笔画到撤销栈
 * 2. 重新渲染画布
 * 3. 更新界面并发送信号
 */
void SignatureLabel::redo()
{
    if (m_redoStack.isEmpty()) return;

    // 从重做栈移动到撤销栈
    m_undoStack.push(m_redoStack.pop());

    // 重新渲染画布（包含重做的笔画）
    renderToCanvas();

    // 更新界面
    update();

    // 发送变化信号
    emit signatureChanged();
    emit undoStateChanged();
}

/// @brief 检查是否可以撤销
bool SignatureLabel::isUndoAvailable() const
{
    return !m_undoStack.isEmpty();
}

/// @brief 检查是否可以重做
bool SignatureLabel::isRedoAvailable() const
{
    return !m_redoStack.isEmpty();
}

/// @brief 检查是否有签名内容
bool SignatureLabel::hasSignature() const
{
    return !m_undoStack.isEmpty() || !m_currentPath.isEmpty();
}

/**
 * @brief 缩放点坐标
 * @param p 逻辑坐标点
 * @return 设备像素坐标点
 *
 * 处理高DPI缩放，确保在不同DPI屏幕上绘制精度一致
 */
QPointF SignatureLabel::scalePoint(const QPoint &p) const
{
    // 获取设备像素比
    qreal dpr = devicePixelRatioF();

    // 转换为设备像素坐标
    return QPointF(p.x() * dpr, p.y() * dpr);
}

/**
 * @brief 渲染到画布
 *
 * 1. 清空画布并填充背景
 * 2. 使用抗锯齿渲染所有笔画
 * 3. 这是双缓冲技术的核心，确保高效绘制
 */
void SignatureLabel::renderToCanvas()
{
    // 重置画布（透明或背景色）
    m_canvas.fill(m_transparentBg ? Qt::transparent : m_bgColor);

    // 如果没有笔画，直接返回
    if (m_undoStack.isEmpty()) return;

    // 创建画布绘制器
    QPainter painter(&m_canvas);
    // 设置高质量渲染提示
    painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);

    // 绘制所有笔画
    for (const SignatureStroke &stroke : m_undoStack) {
        painter.setPen(stroke.pen);
        painter.drawPath(stroke.path);
    }
}

/**
 * @brief 计算签名边界
 * @return 签名内容的边界矩形（逻辑坐标）
 *
 * 1. 计算所有笔画的联合边界
 * 2. 转换为逻辑坐标（考虑DPI缩放）
 */
QRect SignatureLabel::calculateSignatureBounds() const
{
    // 如果没有笔画，返回空矩形
    if (m_undoStack.isEmpty()) return QRect();

    QRect bounds;
    // 遍历所有笔画，计算联合边界
    for (const SignatureStroke &stroke : m_undoStack) {
        bounds = bounds.united(stroke.path.boundingRect().toRect());
    }

    // 获取设备像素比
    qreal dpr = devicePixelRatioF();

    // 转换为逻辑坐标（界面坐标）
    return QRect(bounds.x() / dpr,
                 bounds.y() / dpr,
                 bounds.width() / dpr,
                 bounds.height() / dpr);
}

/**
 * @brief 鼠标按下事件处理
 * @param ev 鼠标事件
 *
 * 1. 左键按下时开始绘制
 * 2. 初始化新路径
 * 3. 清空重做栈
 */
void SignatureLabel::mousePressEvent(QMouseEvent *ev)
{
    // 只响应左键点击
    if (ev->button() == Qt::LeftButton) {
        m_isDrawing = true; // 标记为绘制状态

        // 开始新路径
        m_currentPath = QPainterPath();
        m_lastScaledPoint = scalePoint(ev->pos());
        m_currentPath.moveTo(m_lastScaledPoint);

        // 清除重做栈（开始新绘制时清除历史）
        if (!m_redoStack.isEmpty()) {
            m_redoStack.clear();
            emit undoStateChanged();
        }

        update(); // 触发重绘
    }
}

/**
 * @brief 鼠标移动事件处理
 * @param ev 鼠标事件
 *
 * 1. 绘制状态下添加点到当前路径
 * 2. 更新最后点位置
 * 3. 触发重绘
 */
void SignatureLabel::mouseMoveEvent(QMouseEvent *ev)
{
    if (m_isDrawing) {
        // 获取缩放后的点（高DPI适配）
        QPointF scaledPoint = scalePoint(ev->pos());

        // 添加直线段到路径
        m_currentPath.lineTo(scaledPoint);
        m_lastScaledPoint = scaledPoint;

        update(); // 触发重绘
    }
}

/**
 * @brief 鼠标释放事件处理
 * @param ev 鼠标事件
 *
 * 1. 结束绘制状态
 * 2. 应用平滑处理
 * 3. 保存笔画到撤销栈
 * 4. 渲染到画布
 * 5. 发送变化信号
 */
void SignatureLabel::mouseReleaseEvent(QMouseEvent *ev)
{
    // 只响应左键释放
    if (m_isDrawing && ev->button() == Qt::LeftButton) {
        m_isDrawing = false; // 结束绘制状态

        // 应用平滑处理（如果启用）
        if (m_smoothingLevel > 0) {
            smoothPath(m_currentPath, m_smoothingLevel);
        }

        // 保存笔画（如果路径非空）
        if (!m_currentPath.isEmpty()) {
            SignatureStroke stroke;
            stroke.path = m_currentPath;
            stroke.pen = m_currentPen;
            m_undoStack.push(stroke); // 添加到撤销栈

            // 渲染笔画到画布
            QPainter painter(&m_canvas);
            painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
            painter.setPen(m_currentPen);
            painter.drawPath(m_currentPath);
        }

        // 重置当前路径
        m_currentPath = QPainterPath();
        update(); // 触发重绘

        // 发送变化信号
        emit signatureChanged();
        emit undoStateChanged();
    }
}

/**
 * @brief 绘制事件处理
 * @param event 绘制事件
 *
 * 1. 绘制背景（如果不透明）
 * 2. 绘制签名画布（双缓冲）
 * 3. 实时绘制当前路径（预览效果）
 */
void SignatureLabel::paintEvent(QPaintEvent *event)
{
    Q_UNUSED(event); // 不使用事件参数

    // 创建控件绘制器
    QPainter painter(this);
    painter.setRenderHint(QPainter::SmoothPixmapTransform);

    // 绘制背景（如果不透明）
    if (!m_transparentBg) {
        painter.fillRect(rect(), m_bgColor);
    }

    // 绘制签名画布（双缓冲技术）
    painter.drawPixmap(0, 0, m_canvas);

    // 实时绘制当前路径（预览效果）
    if (m_isDrawing && !m_currentPath.isEmpty()) {
        painter.setPen(m_currentPen);
        painter.setRenderHint(QPainter::Antialiasing); // 启用抗锯齿

        // 转换为逻辑坐标绘制（高DPI适配）
        qreal dpr = devicePixelRatioF();
        QTransform transform;
        transform.scale(1.0/dpr, 1.0/dpr);
        painter.drawPath(transform.map(m_currentPath));
    }
}

/**
 * @brief 大小调整事件处理
 * @param event 大小调整事件
 *
 * 1. 保存原有画布
 * 2. 创建新尺寸的画布
 * 3. 绘制原有内容到新画布
 * 4. 确保签名内容在大小变化时保持完整
 */
void SignatureLabel::resizeEvent(QResizeEvent *event)
{
    // 保存原有签名
    QPixmap oldCanvas = m_canvas;

    // 获取设备像素比
    qreal dpr = devicePixelRatioF();

    // 创建新尺寸的画布
    m_canvas = QPixmap(size() * dpr);
    m_canvas.setDevicePixelRatio(dpr);

    // 填充背景
    m_canvas.fill(m_transparentBg ? Qt::transparent : m_bgColor);

    // 将原有内容绘制到新画布（保持签名内容）
    if (!oldCanvas.isNull()) {
        QPainter painter(&m_canvas);
        painter.setRenderHint(QPainter::SmoothPixmapTransform);
        painter.drawPixmap(0, 0, oldCanvas);
    }

    // 调用基类处理
    QLabel::resizeEvent(event);
}
